package OuterGameParts;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.rmi.RemoteException;
import java.text.DecimalFormat;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import InnerGameParts.CannonBall;
import InnerGameParts.PirateShip;
import InnerGameParts.RemoteList;
import InnerGameParts.Wind;

/**
 * @author Tom
 */
public class GamePanel extends JPanel implements Runnable {


	/**
	 * Things concerning the rendering the images 
	 */
	private Image dbImage;

	private  final BufferedImage mShipImage;
	private  final BufferedImage mWindImage;
	//private final static String SHIP_IMAGE_PATH = "pirateShip.png";
	private final static String SHIP_IMAGE_PATH = "small_pirate_ship_wshadow.png";
	
	private final static String WIND_IMAGE_PATH = "windImage.png";
	private final static int DRAW_BALL_SIZE = 5;

	
	private int mScreenWidth = 1000 ; 
	
	/**
	 * The Outer Shell of the game that handles all the net working and the start up the closing of the
	 * internal program
	 */

	private final static String GAME_VERSION = "Game Version 2.00, created By Steve, Tom & Davy"; 

	private GameClient mClienthell;
	private RemoteList<PirateShip> myShips;
	private RemoteList<CannonBall> myBalls;
	private RemoteList<Wind> myWind;
	private PirateShip playerShip;

	/**
	 * The crap default Id.
	 */
	private static final long serialVersionUID = 1L;

	private volatile boolean running = true;

	/**
	 * interval for about a second.
	 */
	private final static long MAX_STATS_INTERVAL = 1000L;
	private final static int NO_DELAYS_PER_YEILD = 5;
	//private final static long MAX_STATS_INTERVAL = 1000L;

	/**
	 *  NUmber of frames per second
	 */
	private static int NUM_FPS = 75; 


	/**
	 * Variable which gives back statics about how the aimation loop is running. 
	 */
	private long statsInverval = 0L;
	private long prevStatsTime;
	private long totalElapsedTime = 0L;
	private long frameCount = 0;

	private double fpsStore[];
	private long statsCount = 0; 
	private double averageFPS = 0.0;

	private DecimalFormat df =  new DecimalFormat("0.##") ;  // decimal Format 2 d.p.
	private DecimalFormat timedf =  new DecimalFormat("0.####") ;  // decimal Format 4 d.p.
	private int period;


	/**
	 * 
	 * 
	 * All the variable to do with the calculating the 
	 */

	private long overSleepTime;
	private int noDelays = 0;
	private long excess = 0L;

	/**
	 * 
	 * Constructor for the GamePanel. Adds in the outer shell of the program. 
	 * 
	 * @param mOuterShell the outer shell of the program.
	 * @throws IOException 
	 */
	public GamePanel( final GameClient clienthell,
			final RemoteList<PirateShip> theShips,
			final RemoteList<CannonBall> theBalls,
			final RemoteList<Wind> theWind,
			final PirateShip theShip ) throws IOException{

		mShipImage = ImageIO.read( new File(SHIP_IMAGE_PATH));
		mWindImage = ImageIO.read( new File(WIND_IMAGE_PATH));

		this.setBackground(Color.cyan);
		this.setOpaque(true);
		this.setVisible(true);
		this.mClienthell = clienthell;
		this.myShips = theShips;
		this.myBalls = theBalls;
		this.playerShip = theShip;
		this.myWind = theWind;
		this.setEnabled(true);
		this.setFocusable(true);


		this.addKeyListener( new KeyListener(){

			public void keyPressed(final KeyEvent keyPress) {

				interperateKeyPressDown(keyPress);

			}
			public void keyReleased(final KeyEvent keyPress) {

				interperateKeyPressUp(keyPress);

			}


			public void keyTyped(final KeyEvent keyPress) {}

		});

	}

	public void run(){

		long beforeTime = 0L;
		//	long afterTime;
		//long timeDiff;


		while(running){

			// If there has been a remote exception being the server has shut down.
			//Then don't render the screen

			try {
				renderGame();
			} catch (RemoteException e) {
				e.printStackTrace();
				renderCrash();

			}

			paintScreen();
			//repaint();

			//beforeTime = napABit(beforeTime);

			try {
				Thread.sleep(5);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//Thread.yield();






		}

		exitGame();

	}

	/**
	 * 
	 * Decides how long to let this thread sleep for so that other threads can execute.
	 * 
	 * @param beforeTime the clock of the beginning of the update, render, paint, etc
	 * @return returns the new before time. 
	 */
	private long napABit( final long beforeTime){

		final long afterTime = System.nanoTime();
		final long timeDiff = afterTime - beforeTime; 
		final long sleepTime = (period - timeDiff) - overSleepTime;  

		if (sleepTime > 0 ){

			try{
				Thread.sleep(sleepTime / 1000000L );
			}catch(InterruptedException e){
				overSleepTime  = ( System.nanoTime() - afterTime) - sleepTime;
			}

		} else { // frame took longer than the correct period

			excess -= sleepTime ; 
			overSleepTime = 0;

			if(++noDelays >=  NO_DELAYS_PER_YEILD){
				Thread.yield();
				noDelays = 0;
			}

		}



		return afterTime;


	}

	private void renderCrash(){



	}

	public void interperateKeyPressDown(final KeyEvent keyPress){

		
		System.out.println("Key Down");

		switch (keyPress.getKeyCode()){

		case KeyEvent.VK_ESCAPE: // closes down the program

			mClienthell.closeClient();
			break;

		case  KeyEvent.VK_DOWN:
			break;

		case KeyEvent.VK_UP:

			playerShip.requestForward();
			break;

		case KeyEvent.VK_LEFT:

			playerShip.requestLeft();
			break;

		case KeyEvent.VK_RIGHT:
			playerShip.requestRight();
			break;

		case KeyEvent.VK_Q:
			playerShip.requestShootLeft();
			break;

		case KeyEvent.VK_W:
			playerShip.requestShootRight();
			break;

		}



	}

	public void interperateKeyPressUp(final KeyEvent keyPress){

		
		

		switch (keyPress.getKeyCode()){

		case  KeyEvent.VK_DOWN:
			break;

		case KeyEvent.VK_UP:

			playerShip.derequestForward();
			break;

		case KeyEvent.VK_LEFT:

			playerShip.derequestLeft();
			break;

		case KeyEvent.VK_RIGHT:
			playerShip.derequestRight();
			break;

		case KeyEvent.VK_Q:
			playerShip.derequestShootLeft();
			break;

		case KeyEvent.VK_W:
			playerShip.derequestShootRight();
			break;

		}



	}


	/**
	 * Renders the buffered image to the screen
	 */
	private void paintScreen(){

		Graphics g;

		
		
		try{

			g = this.getGraphics();
			
			g.clearRect(0, 0, mScreenWidth,mScreenWidth);
			
			if(g != null && dbImage != null){

				g.drawImage(dbImage, 0,0,  null);
				Toolkit.getDefaultToolkit().sync();
				g.dispose();
				dbImage = null;

			}


		}catch(Exception e ){

			System.err.println("Graphics conetex error" + e.toString());

		}


	}

	public void paint(Graphics g){

		g.setColor(Color.CYAN);
		g.clearRect(0, 0, g.getClipBounds().width, g.getClipBounds().height);	
		if(g != null && dbImage != null){

			g.drawImage(dbImage, 0,0,  null);
			Toolkit.getDefaultToolkit().sync();
			g.dispose();

		}


	}

	private void renderGame() throws RemoteException{


		if(myShips == null || myBalls == null)
			System.err.println("Render Game Null pointer");
		else
		{




			this.dbImage = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_ARGB);
			Graphics  dbGraphics = dbImage.getGraphics();

			dbGraphics.setColor(Color.BLACK);
			dbGraphics.drawChars(GAME_VERSION.toCharArray(), 0, GAME_VERSION.length(), 0, 10);


			myShips.size();
			myShips.size();

			//*
			//draws all the ships to the screen
			for(int count = 0 ; count < myShips.size(); count++){

				//draws all the ships
				dbGraphics.drawImage(renderShip(myShips.get(count)),
						myShips.get(count).getX() - mShipImage.getHeight()/2  , // not a mistake
						myShips.get(count).getY() - mShipImage.getHeight()/2 ,
						null);
			}




			//draws all the cannon balls
			for(int count = 0 ; count < myBalls.size(); count++){


				//System.out.println("NO of balls " + myBalls.size() + " " 
				//		+ count + ": " + myBalls.get(count).getX()
				//		+ " " + myBalls.get(count).getY());
				dbGraphics.setColor(Color.BLACK);
				dbGraphics.fillOval(myBalls.get(count).getX(), myBalls.get(count).getY(), DRAW_BALL_SIZE, DRAW_BALL_SIZE);


			}

			//Renders the Health to the screen


			//Renders the wind the Screen
			BufferedImage windImage = 	renderWind(myWind.get(0));		
			dbGraphics.drawImage(windImage,
					0,
					this.getHeight() - windImage.getHeight(),
					null );


			//Renders the amount of ammo the player has to the screen

			char [] leftAmount = Integer.toString(playerShip.getLeftAmoo()).toCharArray();
			char []  rightAmount = Integer.toString(playerShip.getRightAmoo()).toCharArray();
			dbGraphics.drawChars(leftAmount, 0, leftAmount.length, 0, 20);  
			dbGraphics.drawChars(rightAmount, 0,rightAmount.length, 0, 40);

			//Renders the amount of heath the player has to the screen
			char [] health = Integer.toString(playerShip.getHealth()).toCharArray();
			dbGraphics.drawChars(health, 0, health.length, 0, 60);

			//Draws on the points on the shape;

			int [][] XY = playerShip.createTheXYArray();


			dbGraphics.setColor(Color.RED);
			for(int count = 0 ; count < XY.length -1; count++){

				//dbGraphics.fillOval(XY[count][0], XY[count][1], 10, 10);
				dbGraphics.drawLine(XY[count][0], XY[count][1], XY[count +1][0], XY[count +1][1]);
			}

			//draws the last point and the last line
			//dbGraphics.fillOval(XY[XY.length -1][0], XY[XY.length -1][1], 10, 10);
			dbGraphics.drawLine(XY[XY.length -1][0], XY[XY.length -1][1], XY[0][0], XY[0][1]);
			
			dbGraphics.setColor(Color.BLUE);
			dbGraphics.fillOval(playerShip.getX(), playerShip.getY(), 10, 10);


		}



	}

	private void exitGame(){

		mClienthell.closeClient();

	}

	private BufferedImage renderShip(PirateShip theShip){

		int direction = theShip.getDirection();
		int GRAPHICS_HEIGHT = mShipImage.getHeight(); 
		int GRAPHICS_WIDTH = mShipImage.getHeight();

		BufferedImage shipImage = new BufferedImage(GRAPHICS_WIDTH,	GRAPHICS_HEIGHT,
				BufferedImage.TYPE_INT_ARGB);



		Graphics2D shipGraphics = (Graphics2D)shipImage.getGraphics();

		int circleWidth = 10;

		
		//translate the image to the correct place
		shipGraphics.translate(GRAPHICS_WIDTH/2 - mShipImage.getWidth() /2, GRAPHICS_HEIGHT/2 - mShipImage.getHeight()/ 2);
		
		//rotate the image to the correct place
		shipGraphics.rotate(Math.toRadians(direction),mShipImage.getWidth() /2 , mShipImage.getHeight() /2);
		
		//draws on to the image
		shipGraphics.drawImage(mShipImage, 0, 0, null);

		//draw middle circle
		//shipGraphics.fillOval(GRAPHICS_WIDTH/2 - circleWidth / 2,
		//GRAPHICS_HEIGHT/2 - circleWidth/ 2, circleWidth, circleWidth);
		
		return shipImage;

	}

	private BufferedImage renderWind(Wind theWind){

		BufferedImage renderImage = new BufferedImage(mWindImage.getWidth(),	mWindImage.getHeight(),
				BufferedImage.TYPE_INT_ARGB); 

		Graphics2D wG = (Graphics2D) renderImage.getGraphics();
		wG.rotate( Math.toRadians(theWind.getDirection()), mWindImage.getWidth() / 2, mWindImage.getHeight()/2);//, mWindImage.getWidth() /2, mWindImage.getHeight() /2);


		wG.drawImage(mWindImage,0,0, null);

		return renderImage;

	}

}
